package org.bdware.irp.irplib.crypto;

import org.bdware.irp.irplib.core.IrpMessage;
import org.bdware.irp.irplib.core.IrpMessageEnvelope;
import org.bdware.irp.irplib.exception.IrpMessageDecodeException;
import org.bdware.irp.irplib.exception.IrpMessageEncodeException;
import org.bdware.irp.irplib.util.IrpCommon;
import org.bdware.irp.irplib.util.EncoderUtils;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;

public class IrpMessagePacket {
    public byte[] envelopePacket;
    public byte[] bodyPacket;

    public IrpMessageEnvelope irpMessageEnvelope;

    public IrpMessagePacket () {
        envelopePacket = new byte[IrpCommon.MESSAGE_ENVELOPE_SIZE];
        bodyPacket = null;
        irpMessageEnvelope = new IrpMessageEnvelope();
    }

    public static ArrayList<IrpMessagePacket> irpMessageToPackets(IrpMessage msg) throws IrpMessageEncodeException{
        ArrayList<IrpMessagePacket> messagePackets = new ArrayList<>();
        ByteBuf bf = Unpooled.directBuffer();
        int messageLength = msg.getEncodedMessage().length;
        bf.writeBytes(msg.encodedMessage);
        int totalNumber = messageLength/IrpCommon.MAX_MESSAGE_PACKET_LENGTH + 1;

        while(bf.isReadable()){
            IrpMessagePacket packet = new IrpMessagePacket();
            if(bf.readableBytes() < IrpCommon.MAX_MESSAGE_PACKET_LENGTH){
                packet.bodyPacket = new byte[bf.readableBytes()];
                bf.readBytes(packet.bodyPacket);
            }else{
                packet.bodyPacket = new byte[IrpCommon.MAX_MESSAGE_PACKET_LENGTH];
                bf.readBytes(packet.bodyPacket);
            }
            packet.irpMessageEnvelope.setTruncated(totalNumber > 1);
            packet.irpMessageEnvelope.setEnCrypted(msg.header.getEncryptionFlag());
            packet.irpMessageEnvelope.sequenceNumber = messagePackets.size();
            packet.irpMessageEnvelope.requestId = msg.envelope.requestId;
            packet.irpMessageEnvelope.messageLength = messageLength;
            packet.envelopePacket = packet.irpMessageEnvelope.getEncodeEnvelope();
            messagePackets.add(packet);
        }
        bf.release();
        return messagePackets;
    }

    //todo
/*    public static IrpMessage packetsToIrpMessage(ArrayList<IrpMessagePacket> packetLists) {

    }*/

    /*
    * Note:
    * the packet number by tcp protocol is set to 1 currently, need to optimize
    *
    * Todo: supprt the TCP packet truncated
    */
    public static IrpMessagePacket irpMessageToTCPPackets(IrpMessage msg) throws IrpMessageEncodeException {
        IrpMessagePacket messagePacket = new IrpMessagePacket();
        int messageLength = msg.getEncodedMessage().length;
        if (messageLength > IrpCommon.MAX_MESSAGE_PACKET_LENGTH)
            throw new IrpMessageEncodeException("The message length is more than the TCP packet limited!");
        messagePacket.bodyPacket = msg.encodedMessage;
        messagePacket.irpMessageEnvelope.setTruncated(false);
        messagePacket.irpMessageEnvelope.setEnCrypted(msg.header.getEncryptionFlag());
        messagePacket.irpMessageEnvelope.sequenceNumber = 0;
        messagePacket.irpMessageEnvelope.requestId = msg.envelope.requestId;
        messagePacket.irpMessageEnvelope.messageLength = messageLength;
        messagePacket.envelopePacket = messagePacket.irpMessageEnvelope.getEncodeEnvelope();

        return messagePacket;
    }

    /*
    * Note:
    * the packet number by tcp protocol is set to 1 currently, need to optimize
    *
    * Todo: supprt the TCP packet truncated
    */
    public static IrpMessage packetTCPToIrpMessage(IrpMessagePacket packet) throws IrpMessageDecodeException {
        IrpMessageEnvelope envelope = new IrpMessageEnvelope();
        EncoderUtils.decodeMessageEnvelope(packet.envelopePacket, envelope);
        IrpMessage msg = EncoderUtils.decodeMessage(packet.bodyPacket, 0, envelope);
        msg.envelope = envelope;
        return msg;
    }


    public static IrpMessagePacket bytesToMessagePacket(byte[] packetBytes) throws IrpMessageDecodeException{
        IrpMessagePacket packet = new IrpMessagePacket();
        int bodyLength = packetBytes.length - IrpCommon.MESSAGE_ENVELOPE_SIZE;
        System.arraycopy(packetBytes, 0, packet.envelopePacket, 0, IrpCommon.MESSAGE_ENVELOPE_SIZE);
        EncoderUtils.decodeMessageEnvelope(packet.envelopePacket, packet.irpMessageEnvelope);
        packet.bodyPacket = new byte[bodyLength];
        System.arraycopy(packetBytes, IrpCommon.MESSAGE_ENVELOPE_SIZE, packet.bodyPacket, 0, bodyLength);
        return packet;
    }

    public static byte[] toBytes(IrpMessagePacket packet) {
        byte[] packetBytes = new byte[IrpCommon.MESSAGE_ENVELOPE_SIZE + packet.bodyPacket.length];
        System.arraycopy(packet.envelopePacket, 0, packetBytes, 0, IrpCommon.MESSAGE_ENVELOPE_SIZE);
        System.arraycopy(packet.bodyPacket, 0, packetBytes, IrpCommon.MESSAGE_ENVELOPE_SIZE, packet.bodyPacket.length);
        return packetBytes;
    }

}
